/* Copyright 2008 Bas van den Berg
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <string>

#include "yaffut.h"
#include "myassert.h"
#include "TestSession.h"

#include "VerbParser.h"
#include "Command.h"

using namespace std;
using namespace Chronos;


class TestDefaultCommand : public DefaultCommand {
public:
    TestDefaultCommand() : called(false) {}
    virtual void execute(Session&, const std::string&) { called = true; }
    void clear() { called = false; }

    bool called;
};


class TestCommand : public Command {
public:
    TestCommand(const string& us)
        : Command(us)
        , calledNoArg(false)
        , calledOneArg(false)
        , calledTwoArg(false)
        , arg1("")
        , arg2("")
    {}
    virtual void execute(Session&) {
        calledNoArg = true;
    }
    virtual void execute(Session&, const std::string& arg1_) {
        calledOneArg = true;
        arg1 = arg1_;
    }
    virtual void execute(Session&, const std::string& arg1_, const std::string& arg2_) {
        calledTwoArg = true; 
        arg1 = arg1_;
        arg2 = arg2_;
    }
    bool isCalled() const { return (calledNoArg || calledOneArg || calledTwoArg); }
    void clear() {
	calledNoArg = calledOneArg = calledTwoArg = false;
	arg1 = "";
	arg2 = "";
    }
    bool calledNoArg;
    bool calledOneArg;
    bool calledTwoArg;
    string arg1;
    string arg2;
};


struct VerbParserTest {
	VerbParserTest()
	: session("player1")
    , defaultCmd(new TestDefaultCommand())
    , parser(*defaultCmd)
	{}

	TestSession session;
    TestDefaultCommand* defaultCmd;
    VerbParser parser;
};


TEST(VerbParserTest, testShouldParseWithNoVerbs) {
    parser.parse(session, "bla" );
    ASSERT_TRUE(defaultCmd->called);
}


TEST(VerbParserTest, testShouldParseVerbOnly) {
    TestCommand* testCmd = new TestCommand("look");
    parser.addCommand("look", "", VerbParser::VERB, testCmd);

    parser.parse(session, "look at");
    ASSERT_FALSE(defaultCmd->called);
    ASSERT_FALSE(testCmd->isCalled());

    parser.parse(session, "look at door");
    ASSERT_FALSE(defaultCmd->called);
    ASSERT_FALSE(testCmd->isCalled());

    parser.parse(session, "look");
    ASSERT_FALSE(defaultCmd->called);
    ASSERT_TRUE(testCmd->calledNoArg);
    testCmd->clear();
}


TEST(VerbParserTest, testShouldParseVerbAdj) {
    TestCommand* testCmd = new TestCommand("look at");
    parser.addCommand("look", "at", VerbParser::VERB_ADJ, testCmd);

    parser.parse(session, "look");
    ASSERT_FALSE(testCmd->isCalled());

    parser.parse(session, "look door at");
    ASSERT_FALSE(testCmd->isCalled());

    parser.parse(session, "look at door");
    ASSERT_FALSE(testCmd->isCalled());

    parser.parse(session, "look at");
    ASSERT_TRUE(testCmd->calledNoArg);
}

TEST(VerbParserTest, testShouldParseVerbObj) {
    TestCommand* testCmd = new TestCommand("tickle");
    parser.addCommand("tickle", "", VerbParser::VERB_OBJ, testCmd);

    parser.parse(session, "tickle");
    ASSERT_FALSE(testCmd->isCalled());

    parser.parse(session, "tickle orc");
    ASSERT_TRUE(testCmd->calledOneArg);
    ASSERT_STR_EQUAL("orc", testCmd->arg1);
}


TEST(VerbParserTest, testShouldParseVerbAdjObj) {
    TestCommand* testCmd = new TestCommand("look at");
    parser.addCommand("look", "at", VerbParser::VERB_ADJ_OBJ, testCmd);

    parser.parse(session, "look");
    ASSERT_FALSE(testCmd->isCalled());

    parser.parse(session, "look door at");
    ASSERT_FALSE(testCmd->isCalled());

    parser.parse(session, "look at");
    ASSERT_FALSE(testCmd->isCalled());

    parser.parse(session, "look at big dwarf");
    ASSERT_TRUE(testCmd->calledOneArg);
    ASSERT_STR_EQUAL("big dwarf", testCmd->arg1);
}


TEST(VerbParserTest, testShouldParseVerbObjAdjObj) {
    TestCommand* testCmd = new TestCommand("put in");
    parser.addCommand("put", "in", VerbParser::VERB_OBJ_ADJ_OBJ, testCmd);

    parser.parse(session, "put");
    ASSERT_FALSE(testCmd->isCalled());

    parser.parse(session, "put in");
    ASSERT_FALSE(testCmd->isCalled());

    parser.parse(session, "put key in");
    ASSERT_FALSE(testCmd->isCalled());

    parser.parse(session, "put in lock");
    ASSERT_FALSE(testCmd->isCalled());
    
    parser.parse(session, "put iron key in golden lock");
    ASSERT_FALSE(testCmd->calledOneArg);
    ASSERT_TRUE(testCmd->calledTwoArg);
    ASSERT_STR_EQUAL("iron key", testCmd->arg1);
    ASSERT_STR_EQUAL("golden lock", testCmd->arg2);
}


TEST(VerbParserTest, testShouldParseVerbObjObj) {
    TestCommand* testCmd = new TestCommand("alias");
    parser.addCommand("alias", "", VerbParser::VERB_OBJ_OBJ, testCmd);

    parser.parse(session, "alias");
    ASSERT_FALSE(testCmd->isCalled());

    parser.parse(session, "alias key");
    ASSERT_FALSE(testCmd->isCalled());

    parser.parse(session, "alias key value");
    ASSERT_TRUE(testCmd->calledTwoArg);
	ASSERT_STR_EQUAL("key", testCmd->arg1);
	ASSERT_STR_EQUAL("value", testCmd->arg2);
	testCmd->clear();

    parser.parse(session, "alias sfc  search  for  clues  ");
    ASSERT_TRUE(testCmd->calledTwoArg);
	ASSERT_STR_EQUAL("sfc", testCmd->arg1);
	ASSERT_STR_EQUAL("search for clues", testCmd->arg2);
}


TEST(VerbParserTest, testShouldselectCorrectCommand) {
    TestCommand* testCmd1 = new TestCommand("sleep");
    parser.addCommand("sleep", "", VerbParser::VERB, testCmd1);
    TestCommand* testCmd2 = new TestCommand("look at");
    parser.addCommand("look", "at", VerbParser::VERB_ADJ_OBJ, testCmd2);
    TestCommand* testCmd3 = new TestCommand("attack");
    parser.addCommand("attack", "", VerbParser::VERB_OBJ, testCmd3);

    parser.parse(session, "sleep");
    ASSERT_TRUE(testCmd1->calledNoArg);
    ASSERT_FALSE(testCmd2->isCalled());
    ASSERT_FALSE(testCmd3->isCalled());
    testCmd1->clear();

    parser.parse(session, "look at huge, green tree");
    ASSERT_FALSE(testCmd1->isCalled());
    ASSERT_TRUE(testCmd2->calledOneArg);
    ASSERT_STR_EQUAL("huge, green tree", testCmd2->arg1);
    ASSERT_FALSE(testCmd3->isCalled());
    testCmd2->clear();

    parser.parse(session, "attack monster");
    ASSERT_FALSE(testCmd1->isCalled());
    ASSERT_FALSE(testCmd2->isCalled());
    ASSERT_TRUE(testCmd3->calledOneArg);
    ASSERT_STR_EQUAL("monster", testCmd3->arg1);
    testCmd3->clear();
}


TEST(VerbParserTest, testShouldUseCorrectAdj) {
    TestCommand* testCmd2 = new TestCommand("look at");
    parser.addCommand("look", "at", VerbParser::VERB_ADJ_OBJ, testCmd2);
    TestCommand* testCmd3 = new TestCommand("look up");
    parser.addCommand("look", "up", VerbParser::VERB_ADJ, testCmd3);
    TestCommand* testCmd1 = new TestCommand("look");
    parser.addCommand("look", "", VerbParser::VERB, testCmd1);

    parser.parse(session, "look under");
    ASSERT_FALSE(testCmd1->isCalled());
    ASSERT_FALSE(testCmd2->isCalled());
    ASSERT_FALSE(testCmd3->isCalled());

    parser.parse(session, "look at");
    ASSERT_FALSE(testCmd1->isCalled());
    ASSERT_FALSE(testCmd2->isCalled());
    ASSERT_FALSE(testCmd3->isCalled());

    parser.parse(session, "look up at");
    ASSERT_FALSE(testCmd1->isCalled());
    ASSERT_FALSE(testCmd2->isCalled());
    ASSERT_FALSE(testCmd3->isCalled());

    parser.parse(session, "look at up");
    ASSERT_FALSE(testCmd1->isCalled());
    ASSERT_TRUE(testCmd2->calledOneArg);
    ASSERT_FALSE(testCmd3->isCalled());
    testCmd2->clear();

    parser.parse(session, "look");
    ASSERT_TRUE(testCmd1->isCalled());
    ASSERT_FALSE(testCmd2->isCalled());
    ASSERT_FALSE(testCmd3->isCalled());
    testCmd1->clear();
}


TEST(VerbParserTest, testShouldParseCorrectly) {
    TestCommand* testCmd2 = new TestCommand("look at");
    parser.addCommand("look", "at", VerbParser::VERB_ADJ_OBJ, testCmd2);
    TestCommand* testCmd1 = new TestCommand("look");
    parser.addCommand("look", "", VerbParser::VERB_OBJ, testCmd1);
    TestCommand* testCmd3 = new TestCommand("look up");
    parser.addCommand("look", "up", VerbParser::VERB_ADJ, testCmd3);

    parser.parse(session, "look");
    ASSERT_FALSE(testCmd1->isCalled());
    ASSERT_FALSE(testCmd2->isCalled());
    ASSERT_FALSE(testCmd3->isCalled());

    parser.parse(session, "look door");
    ASSERT_TRUE(testCmd1->calledOneArg);
    ASSERT_STR_EQUAL("door", testCmd1->arg1);
    ASSERT_FALSE(testCmd2->isCalled());
    ASSERT_FALSE(testCmd3->isCalled());
    testCmd1->clear(); 

    parser.parse(session, "look at");
    ASSERT_FALSE(testCmd1->isCalled());
    ASSERT_FALSE(testCmd2->isCalled());
    ASSERT_FALSE(testCmd3->isCalled());

    parser.parse(session, "look up");
    ASSERT_FALSE(testCmd1->isCalled());
    ASSERT_FALSE(testCmd2->isCalled());
    ASSERT_TRUE(testCmd3->calledNoArg);
    testCmd3->clear(); 

    parser.parse(session, "look at huge gnome");
    ASSERT_FALSE(testCmd1->isCalled());
    ASSERT_TRUE(testCmd2->calledOneArg);
    ASSERT_STR_EQUAL("huge gnome", testCmd2->arg1);
    ASSERT_FALSE(testCmd3->isCalled());
    testCmd2->clear(); 
}


TEST(VerbParserTest, testParserShouldPreferAdjOverArgument) {
    TestCommand* testCmd2 = new TestCommand("get");
    parser.addCommand("get", "", VerbParser::VERB_OBJ, testCmd2);
    TestCommand* testCmd1 = new TestCommand("get all");
    parser.addCommand("get", "all", VerbParser::VERB_ADJ, testCmd1);
    
    parser.parse(session, "get all");
    ASSERT_TRUE(testCmd1->isCalled());
    ASSERT_FALSE(testCmd2->isCalled());
}

